package com.hillky.config;

import com.hillky.IdGenerator;
import com.hillky.ProtocolConfig;
import com.hillky.RegistryConfig;
import com.hillky.compress.Compressor;
import com.hillky.compress.CompressorFactory;
import com.hillky.loadbalancer.LoadBalancer;
import com.hillky.serialize.Serializer;
import com.hillky.serialize.SerializerFactory;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
@Slf4j
public class XmlResolver {

    /**
     * 从配置文件读取配置信息 我们不使用dom4j
     * @param configuration 配置实例
     */
    public void loadFormXml(Configuration configuration) {
        try {
            // 1.创建document对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 禁用DTD校验：可以通过调用setValidating(false)方法来禁用DTD校验。
            factory.setValidating(false);
            // 禁用外部实体解析：可以通过调用setFeature(String name, boolean value)方法并将“http://apache.org/xml/features/nonvalidating/load-external-dtd”设置为“false”来禁用外部实体解析。
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

            DocumentBuilder builder = factory.newDocumentBuilder();

            InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("yrpc.xml");
            Document doc = builder.parse(inputStream);

            // 2. 获取一个xpath解析器
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xPath = xPathFactory.newXPath();

            // 3. 解析所有标签
            configuration.setPort(resolverPort(doc, xPath));

            configuration.setAppName(resolverAppName(doc, xPath));

            configuration.setIdGenerator(resolverIdGenerator(doc,xPath));

            configuration.setRegistryConfig(resolverRegistryConfig(doc,xPath));

             // 需要独立的去处理
            // 处理使用的压缩方式和序列化方式
            configuration.setCompressType(resolverCompressType(doc,xPath));
            configuration.setSerializeType(resolverSerializeType(doc,xPath));

            // 配置新的压缩方式和序列化方式，并将其纳入工厂中
            ObjectWrapper<Compressor> compressorObjectWrapper = resolverCompressor(doc, xPath);
            CompressorFactory.addCompressor(compressorObjectWrapper);

            ObjectWrapper<Serializer> serializerObjectWrapper = resolverSerializer(doc, xPath);
            SerializerFactory.addSerializer(serializerObjectWrapper);


            configuration.setLoadBalancer(resolverLoadBalancer(doc,xPath));

            // 如果有新增的标签，这里继续修改

        } catch (ParserConfigurationException | IOException | SAXException e) {
            log.info("解析xml配置发下生异常，将选用默认配置");

        }

    }



    /**
     * 解析端口号
     * @param doc 文档对象
     * @param xPath xpath
     * @return 端口号
     */
    private int resolverPort(Document doc, XPath xPath) {
        String expression="/configuration/port";
        String res = parseString(doc, xPath, expression);
        return Integer.parseInt(res);
    }

    /**
     * 解析应用名称
     * @param doc
     * @param xPath
     * @return
     */
    private String resolverAppName(Document doc, XPath xPath) {
        String expression="/configuration/appName";
        String res = parseString(doc, xPath, expression);
        return res;
    }


    /**
     * 解析负载均衡器
     * @param doc
     * @param xPath
     * @return 负载均衡器
     */
    private LoadBalancer resolverLoadBalancer(Document doc, XPath xPath) {
        String expression="/configuration/loadBalancer";
        return parseObject(doc,xPath,expression,null);
    }

    /**
     * 解析具体id生成器
     * @param doc
     * @param xPath
     * @return
     */
    private IdGenerator resolverIdGenerator(Document doc, XPath xPath) {

        String expression="/configuration/idGenerator";
        String aClass = parseString(doc, xPath, expression, "class");
        String dataCenterId = parseString(doc, xPath, expression, "dataCenterId");
        String machineId = parseString(doc, xPath, expression, "MachineId");

        Class<?> clazz = null;
        Object instance=null;
        try {
            clazz = Class.forName(aClass);
            instance = clazz.getConstructor(new Class[]{long.class, long.class})
                    .newInstance(Long.parseLong(dataCenterId), Long.parseLong(machineId));
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return (IdGenerator)instance;
    }

    /**
     * 解析注册中心
     * @param doc
     * @param xPath
     * @return
     */
    private RegistryConfig resolverRegistryConfig(Document doc, XPath xPath) {
        String expression="/configuration/registry";
        String url = parseString(doc, xPath, expression, "url");
        return new RegistryConfig(url);
    }



    /**
     * 解析压缩的具体实现
     * @param doc
     * @param xPath
     * @return
     */
    private ObjectWrapper<Compressor> resolverCompressor(Document doc, XPath xPath) {
        String expression="/configuration/compressor";
        Compressor compressor = parseObject(doc, xPath, expression, null);
        Byte code = Byte.valueOf(parseString(doc, xPath, expression, "code"));
        String name = parseString(doc, xPath, expression, "name");
        ObjectWrapper<Compressor> compressorObjectWrapper = new ObjectWrapper<>(code,name,compressor);
        return compressorObjectWrapper;
    }

    /**
     * 解析压缩算法名称
     * @param doc
     * @param xPath
     * @return 返回算法名称
     */
    private String resolverCompressType(Document doc, XPath xPath) {
        String expression="/configuration/compressType";
        return parseString(doc, xPath, expression, "type");
    }

    /**
     * 解析序列化类型
     * @param doc
     * @param xPath
     * @return
     */
    private String resolverSerializeType(Document doc, XPath xPath) {
        String expression="/configuration/serializeType";
        return parseString(doc, xPath, expression, "type");
    }


    /**
     * 解析具体序列化器
     * @param doc
     * @param xPath
     * @return
     */
    private ObjectWrapper<Serializer> resolverSerializer(Document doc, XPath xPath) {
        String expression="/configuration/serializer";
        Serializer serializer = parseObject(doc, xPath, expression, null);
        Byte code = Byte.valueOf(parseString(doc, xPath, expression, "code"));
        String name = parseString(doc, xPath, expression, "name");
        ObjectWrapper<Serializer> serializerObjectWrapper = new ObjectWrapper<>(code,name,serializer);
        return serializerObjectWrapper;
    }






    /**
     * 获得一个节点属性的值
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @param expression xpath表达式
     * @param attributeName 节点名称
     * @return 节点的值
     */
    private String parseString(Document doc, XPath xPath,String expression,String attributeName) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮我获取节点
            Node targetNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            return targetNode.getAttributes().getNamedItem(attributeName).getNodeValue();

        } catch (XPathExpressionException e) {
            e.printStackTrace();
            log.error("解析表达式时发生问题");
        }
        return null;
    }

    /**
     * 获得一个节点文本值
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @param expression xpath表达式
     * @return 节点的值
     */
    private String parseString(Document doc, XPath xPath, String expression) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮我获取节点
            Node targetNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            return targetNode.getTextContent();
        } catch (XPathExpressionException e) {
            e.printStackTrace();
            log.error("解析表达式时发生问题");
        }
        return null;
    }



    /**
     * 解析一个节点，返回一个实例
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @param expression xpath表达式
     * @param paramType 参数列表
     * @param params 参数
     * @param <T>
     * @return 配置的实例
     * @throws XPathExpressionException
     */
    private <T> T parseObject(Document doc, XPath xPath,String expression,Class<?>[] paramType,Object... params) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮我获取节点
            Node targetNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            String className=targetNode.getAttributes().getNamedItem("class").getNodeValue();
            Class<?> aClass = Class.forName(className);
            Object instance=null;
            if(paramType==null){
                instance = aClass.getConstructor().newInstance();
            }else{
                instance= aClass.getConstructor(paramType).newInstance(params);
            }
            return (T) instance;
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | XPathExpressionException e) {
            e.printStackTrace();
            log.error("解析表达式时发生问题");
        }
        return null;
    }
}
